package com.weiyi.weiman.service.comics.impl;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.weiyi.weiman.domain.Const.ComicsConst;
import com.weiyi.weiman.domain.Const.UserConst;
import com.weiyi.weiman.domain.dto.ComicsChapterDTO;
import com.weiyi.weiman.domain.dto.ComicsPictureDTO;
import com.weiyi.weiman.domain.status.EntityStatus;
import com.weiyi.weiman.domain.status.ResultStatus;
import com.weiyi.weiman.exception.APIException;
import com.weiyi.weiman.mapper.comics.*;
import com.weiyi.weiman.mapper.user.UserBrowseHistoryMapper;
import com.weiyi.weiman.mapper.user.UserBuyMapper;
import com.weiyi.weiman.service.comics.ComicsService;
import com.weiyi.weiman.service.comics.ComicsShowService;
import com.weiyi.weiman.utils.FastDFSUtils;
import com.weiyi.weiman.utils.JWTUtils;
import com.weiyi.weiman.utils.RedisUtils;
import comics.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import user.UserBrowseHistory;
import user.UserBuy;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author zhang
 * @Date 2021/5/9 下午9:22
 * @Description 漫画展示服务类
 * @Version 1.0
 **/
@Slf4j
@Service
public class ComicsShowServiceImpl implements ComicsShowService {
    @Autowired
    private BaseComicsMapper baseComicsMapper;
    @Autowired
    private ComicsPictureMapper pictureMapper;
    @Autowired
    private UserBuyMapper userBuyMapper;
    @Autowired
    private ComicsCarouselMapper carouselMapper;
    @Autowired
    private ComicsService comicsService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private FastDFSUtils fastDFSUtils;

    /**
     * 通过名称查找漫画
     * @param comicsName
     * @return
     */
    @Override
    public List<BaseComics> selectByName(String comicsName) {
        if (StringUtils.isEmpty(comicsName))
            throw new APIException("搜索漫画名称为空");
        QueryWrapper<BaseComics> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("comics_name",comicsName.replace("","%"))
                .eq("comics_status", EntityStatus.STATUS_NORMAL.getCode())
                .select("comics_id", "comics_name");
        List<BaseComics> comicsList = baseComicsMapper.selectList(queryWrapper);
        System.out.println(comicsList);
        return comicsList;
    }

    /**
     * 处理获取漫画的业务方法
     * @param chapterId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<ComicsPictureDTO> showComics(long chapterId, String token) {
        if (StringUtils.isEmpty(chapterId+""))
            throw new APIException("章节为空");

        //获得章节信息
        ComicsChapter comicsChapter = comicsService.getChapter(chapterId);

        // 获取漫画图片
        List<ComicsPictureDTO> list = null;
        String userId = null;
        if (comicsChapter.getChapterCharge() == EntityStatus.CHAPTER_FREE.getCode()) {
            list = getPictureDTO(comicsChapter);
        }
        if (comicsChapter.getChapterCharge() == EntityStatus.CHAPTER_CHARGE.getCode()) {
            if (JWTUtils.validateJWT(token)) {
                //查询是否有购买
                userId = JWTUtils.parseJWT(token).getSubject();
                if (isPurchased(userId, chapterId+"")) {
                    list = getPictureDTO(comicsChapter);
                } else {
                    log.error("未找到章节:{}信息", chapterId);
                    throw new APIException(ResultStatus.NON_PURCHASED_ERROR);
                }
            }
        }

        //如果用户有登陆，则将保存浏览记录到缓存中
        if (!StringUtils.isEmpty(token)) {
            userId = JWTUtils.parseJWT(token).getSubject();
            UserBrowseHistory history = new UserBrowseHistory();
            history.setComicsId(comicsChapter.getComicsId());
            history.setChapterId(comicsChapter.getChapterId());
            history.setUserId(Long.parseLong(userId));
            history.setDeleted(0);
            history.setGwtCreate(new Date());
            String historyKey = UserConst.USER_BROWSE_PRE + userId;
            redisUtils.hPut(historyKey, history.getComicsId()+"", JSON.toJSONString(history));
            redisUtils.setExpire(historyKey, 48, TimeUnit.HOURS);
        }

        //观看量增加
        String comicsKey = ComicsConst.COMICS_REDIS_PRE+comicsChapter.getComicsId();
        if (redisUtils.hasKey(comicsKey)) {
            redisUtils.hIncrBy(comicsKey, "comicsVisitor", 1);
        }
        log.info("章节:{}漫画图片请求成功", chapterId);
        return list;
    }

    /**
     * 获取某章节的信息
     * @param chapterId
     * @return
     */
    @Override
    public ComicsChapterDTO getChapter(long chapterId) {
        //获得章节信息
        ComicsChapter comicsChapter = comicsService.getChapter(chapterId);
        ComicsChapterDTO comicsChapterDTO = new ComicsChapterDTO();
        comicsChapterDTO.setChapterId(comicsChapter.getChapterId());
        comicsChapterDTO.setChapterName(comicsChapter.getChapterName());
        comicsChapterDTO.setNextChapterId(comicsChapter.getNextChapterId());
        comicsChapterDTO.setPreviousChapterId(comicsChapter.getPreviousChapterId());
        return comicsChapterDTO;
    }

    /**
     * 获取某漫画的章节信息，分页查询
     * @param comicsId
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public List<ComicsChapter> getChapterList(long comicsId, int pageNum, int pageSize) {
        comicsService.getComics(comicsId);
        if (comicsService.setChapterListRedis(comicsId)) {
            String chapterKey = ComicsConst.CHAPTER_LIST_REDIS_PRE + comicsId;
            List<ComicsChapter> chapterList = new ArrayList<>();
            List<String> list = redisUtils.lRange(chapterKey, (pageNum - 1) * pageSize, pageNum * pageSize - 1);
            for (String chapter : list) {
                ComicsChapter comicsChapter = JSON.parseObject(chapter, ComicsChapter.class);
                try {
                    if (!StringUtils.isEmpty(comicsChapter.getChapterCover()))
                        comicsChapter.setChapterCover(fastDFSUtils.getThumbImageTokenUrl(comicsChapter.getChapterCover()));
                } catch (Exception e) {
                    log.error("章节:{}封面下载失败", comicsChapter.getChapterId());
                    throw new APIException("章节封面下载失败");
                }
                chapterList.add(comicsChapter);
            }
            return chapterList;
        }
        return null;
    }

    /**
     * 获取漫画主页信息
     * @param comicsId
     * @return
     */
    @Override
    public Comics getHomePage(long comicsId, String referer) {
        Comics comics = comicsService.getComics(comicsId);
        String comicsKey = ComicsConst.COMICS_REDIS_PRE + comicsId;
        if ("search".equals(referer))
            redisUtils.hIncrBy(comicsKey, "comicsSearch", 1);
        try {
            comics.setComicsCover(fastDFSUtils.getThumbImageTokenUrl(comics.getComicsCover()));
        } catch (Exception e) {
            log.error("漫画{}封面下载失败", comics.getComicsId());
            throw new APIException("漫画封面下载失败");
        }
        return comics;
    }

    /**
     * 获取轮播图信息
     * @return
     */
    @Override
    @Cacheable(cacheNames = "comicsService:chapter:list")
    public List<ComicsCarousel> getCarouselList() {
        if (!redisUtils.hasKey(ComicsConst.COMICS_CAROUSEL)) {
            QueryWrapper<ComicsCarousel> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("carousel_status", EntityStatus.STATUS_NORMAL.getCode());
            List<ComicsCarousel> carouselList = carouselMapper.selectList(queryWrapper);
            if (!Objects.isNull(carouselList)) {
                //保存轮播图的数量
                redisUtils.set(ComicsConst.COMICS_CAROUSEL_NUM, carouselList.size()+"");
                for (ComicsCarousel comicsCarousel : carouselList) {
                    redisUtils.lRightPush(ComicsConst.COMICS_CAROUSEL, JSON.toJSONString(comicsCarousel));
                }
            } else {
                throw new APIException(ResultStatus.NON_CAROUSEL_ERROR);
            }
        }
        int count = Integer.parseInt(redisUtils.get(ComicsConst.COMICS_CAROUSEL_NUM));
        HashSet<Integer> randomIndex = getRandomIndex(ComicsConst.CAROUSEL_SHOW_NUM, count);
        List<ComicsCarousel> list = new ArrayList<>();
        for (Integer index : randomIndex) {
            String carouselJson = redisUtils.lIndex(ComicsConst.COMICS_CAROUSEL, index);
            if (!StringUtils.isEmpty(carouselJson)) {
                ComicsCarousel carousel = JSON.parseObject(carouselJson, ComicsCarousel.class);
                try {
                    carousel.setCarouselCover(fastDFSUtils.getTokenUrl(carousel.getCarouselCover()));
                } catch (Exception e) {
                    log.error("漫画:{},轮播图{}下载封面失败", carousel.getComicsId(), carousel.getCarouselId());
                    throw new APIException("轮播图下载封面失败");
                }
                list.add(carousel);
            }
        }
        redisUtils.setExpire(ComicsConst.COMICS_CAROUSEL, 24, TimeUnit.HOURS);
        redisUtils.setExpire(ComicsConst.COMICS_CAROUSEL_NUM, 24, TimeUnit.HOURS);
        return list;
    }

    /**
     * 获取某章节的漫画图片
     * @param comicsChapter
     * @return
     */
    private List<ComicsPictureDTO> getPictureDTO(ComicsChapter comicsChapter) {
        List<ComicsPictureDTO> list = new ArrayList<>();
        String chapterKey = ComicsConst.CHAPTER_REDIS_PRE + comicsChapter.getChapterId();
        if (!redisUtils.hasKey(chapterKey)) {
            //获取漫画章节和章节图片信息，存入数据传输对象中
            ComicsPictureDTO comicsPictureDTO = new ComicsPictureDTO();
            QueryWrapper<ComicsPicture> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("chapter_id", comicsChapter.getChapterId()).orderByAsc("picture_num");
            List<ComicsPicture> pictureList = pictureMapper.selectList(queryWrapper);
            for (ComicsPicture picture : pictureList) {
                comicsPictureDTO.setPictureId(picture.getPictureId());
                comicsPictureDTO.setPictureNum(picture.getPictureNum());
                comicsPictureDTO.setPictureAddress(picture.getPictureAddress());
                //将信息存入redis中并设置过期时间
                redisUtils.lRightPush(chapterKey, JSON.toJSONString(comicsPictureDTO));
            }
        }
        // 从redis中获取数据
        List<String> pictures = redisUtils.lRange(chapterKey, 0, -1);
        //重新更新过期时间
        redisUtils.setExpire(chapterKey, 24, TimeUnit.HOURS);
        for (String picture : pictures) {
            ComicsPictureDTO comicsPictureDTO = JSON.parseObject(picture, ComicsPictureDTO.class);
            try {
                comicsPictureDTO.setPictureAddress(fastDFSUtils.getTokenUrl(comicsPictureDTO.getPictureAddress()));
            } catch (Exception e) {
                throw new APIException("图片下载失败");
            }
            list.add(comicsPictureDTO);
        }
        return list;
    }

    /**
     * 判断用户是否购买了章节
     * @param userId
     * @param chapterId
     * @return
     */
    private boolean isPurchased(String userId, String chapterId) {
        if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(chapterId))
            return false;
        String userBuyKey = UserConst.USER_BUY_PRE + userId;
        if (!redisUtils.hasKey(userBuyKey)) {
            QueryWrapper<UserBuy> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId).eq("chapter_id", chapterId);
            List<UserBuy> userBuys = userBuyMapper.selectList(queryWrapper);
            for (UserBuy userBuy : userBuys) {
                redisUtils.hPut(userBuyKey, userBuy.getChapterId()+"", JSON.toJSONString(userBuy));
            }
        }
        return redisUtils.hExists(userBuyKey, chapterId);
    }

    private HashSet<Integer> getRandomIndex(int count, int num) {
        Random ran = new Random();
        HashSet hs = new HashSet();
        for(;;){
            int tmp = ran.nextInt(num);
            hs.add(tmp);
            if(hs.size() == num || hs.size() == count) break;
        }
        return hs;
    }
}
